<?php
/**
 * DotHerbert
 *
 * @package    objects
 * @subpackage factory
 * @copyright  2009 extreme-programmers
 * @license    http://dotherbert.extreme-programmers.com/license   BSD License
 * @version    $Id:$
 */

namespace dotherbert\objects\factory;

/**
 * Xml object factory
 *
 * @package    objects
 * @subpackage factory
 * @author     Johannes Trink <johannes.trink@extreme-programmers.com>
 */
class XmlObjectFactory extends AbstractObjectFactory implements ObjectFactory
{
    private $objects = array(), $configPath, $parser, $objectsDefinition;

    /**
     * Constructor
     *
     * @param string $configPath
     */
    public function __construct($configPath)
    {
        $this->configPath = $configPath;
    }

    /**
     * Get default parser
     *
     * @return  dotherbert\objects\factory\parser\Parser
     */
    public function getDefaultParser()
    {
        return new parser\XmlParser($this->configPath);
    }

    /**
     * Get object
     *
     * @param string $id
     * @return object
     */
    public function getObject($id)
    {
        if (false === isset($this->objects[$id])) {
            $objectsDefinition = $this->getObjectsDefinition();
            if (false === isset($objectsDefinition['object'][$id])) {
                throw new ObjectFactoryException('no definition found for id \'' . $id . '\'');
            }
            $objectDefinition = $objectsDefinition['object'][$id];

            $class = $objectDefinition['class'];

            if (isset($objectDefinition['factory-method'])) {
                $method = $objectDefinition['factory-method'];
                $object = $class::$method();
            } else {
                $object = new $class();
            }

            if (isset($objectDefinition['scope'])) {
                $scope = $objectDefinition['scope'];
            } else {
                $scope = 'singleton';
            }

            if (isset($objectDefinition['property'])) {
                $this->injectProperties($object, $objectDefinition['property']);
            }

            switch ($scope) {
                case 'singleton':
                    $this->objects[$id] = $object;
                    break;
                case 'prototype':
                    return $object;
                    break;
            }
        }
        return $this->objects[$id];
    }

    /**
     * Get objects definition
     *
     * @return array
     */
    private function getObjectsDefinition()
    {
        if (null === $this->objectsDefinition) {
            $this->objectsDefinition = $this->getParser()->parse();
        }
        return $this->objectsDefinition;
    }

    /**
     * Inject properties
     *
     * @param object $object
     * @param array  $propertyDefintion
     * return void
     */
    private function injectProperties($object, array $propertyDefinition)
    {
        foreach ($propertyDefinition as $name => $definition) {
            $method = 'set' . ucfirst($name);

            if (isset($definition['value'])) {
                if (is_array($definition['value'])) {
                    $value = $definition['value']['#cdata'];
                    if (isset($definition['value']['type'])) {
                        settype($value, $definition['value']['type']);
                    }
                } else {
                    $value = $definition['value'];
                }
            } elseif (isset($definition['ref'])) {
                print_r($definition);
                if (is_array($definition['ref'])) {
                    $refId = $definition['ref']['object'];
                } else {
                    $refId = $definition['ref'];
                }
                $value = $this->getObject($refId);
            }
            $object->$method($value);
        }
    }
}